#pragma once

#include <iostream>
#include <queue>
#include <pthread.h>
#include "mutex.hpp"
#include "Cond.hpp"

namespace BlockQueueModule
{
    static const int gcap = 10;
    using namespace MutexModule;
    using namespace CondModule;

    template <typename T>
    class BlockQueue
    {
    private:
        bool IsFull() { return _queue.size() == _cap; }
        bool IsEmpty() { return _queue.empty(); }

    public:
        BlockQueue(int cap = gcap) : _cap(cap), _cwait_num(0), _pwait_num(0)
        {
        }

        void Equeue(const T &in) // 生产者
        {
            LockGuard lockguard(_mutex);
            // 先判断是否满了，如果满了，就等待
            while (IsFull())
            {
                printf("生产者进入等待\n");
                _pwait_num++; // 等待的生产者数量增加
                _productor_cond.Wait(_mutex);
                _pwait_num--;
                printf("生产者退出等待\n");
            }
            // 走到这里，一定是不满的，所以我们就可以执行插入

            _queue.push(in);

            // 此时我们判断，是否有消费者还在等待，如果是的话，就可以唤醒消费者了
            if (_cwait_num > 0)
            {
                printf("唤醒一个消费者\n");
                _consumer_cond.Signal();
            }
        }

        void Pop(T *out) // 消费者
        {
            LockGuard lockguard(_mutex);
            // 先判断是否满了，如果满了，就等待
            while (IsEmpty())
            {
                printf("消费者进入等待\n");
                _cwait_num++; // 等待的消费者数量增加
                _consumer_cond.Wait(_mutex);
                _cwait_num--;
                printf("消费者退出等待\n");
            }
            // 走到这里，一定是不满的，所以我们就可以执行插入
            *out = _queue.front();
            _queue.pop();

            // 此时我们判断，是否有消费者还在等待，如果是的话，就可以唤醒消费者了
            if (_pwait_num > 0)
            {
                printf("唤醒一个生产者\n");
                _productor_cond.Signal();
            }
        }
        ~BlockQueue()
        {
        }

    private:
        std::queue<T> _queue; // 临界区资源
        Mutex _mutex;         // 互斥锁
        int _cap;             // bq最大容量
        Cond _productor_cond; // 生产者条件变量
        Cond _consumer_cond;  // 消费者条件变量

        int _cwait_num; // 等待的消费者数目
        int _pwait_num; // 等待的生产者数目
    };
}